// String hashing helper
String.prototype.hashCode = function(){
    var hash = 0, i, char;
    if (this.length == 0) return hash;
    for (i = 0; i < this.length; i++) {
        char = this.charCodeAt(i);
        hash = ((hash<<5)-hash)+char;
        hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
};

function Renoise(code) {
	var ws
	var messageHandlers = []

	return {
		connect: connect,
		getStatus: getStatus
	}
	
	function connect(uri){
		ws = new WebSocket(uri || "ws://localhost:9769"); 
		ws.onmessage = function(e) { 
			console.log(e)
			for(i in messageHandlers) { 	
				messageHandlers[i](JSON.parse(e.data)) 
			} 
		}
		ws.onerror = function(e) { console.log('Error: ', e) }
		ws.onopen = function(e) { code(getRns(ws)) }
	}
	
	function getStatus(){
		return ws.readyState
	}
	
	function getRns(ws) {
		return {
			run: run,
			onMessage: function (callback) { messageHandlers.push(callback) },
			observe: observe,
			unobserve: unobserve
		}
		function run(code) { ws.send(code) }
		function observe(value, wrapper) {
			run(
				'_notifiers["' + value.hashCode() + '"] = function(event) ' + '$$({'+wrapper+' = '+value+', event = event}) end;' +
				value + '_observable:add_notifier(_notifiers["' + value.hashCode() + '"])'
			) 
		}

		function unobserve(value) {
			run(
				'if (' + value + '_observable:has_notifier(_notifiers["' + value.hashCode() + '"])) then ' +
				value + '_observable:remove_notifier(_notifiers["' + value.hashCode() + '"]) '  +
				'end;' +
				'table.remove(_notifiers, "' + value.hashCode() + '")'
			) 
		}
	}
}